#include <amxmodx>

#define VERSION "0.2.1"

#define FIRST_PLAYER_ID	1
#define MAX_PLAYERS 32
#define IsPlayer(%0)	( FIRST_PLAYER_ID <= %0 <= g_iMaxPlayers )

enum _:Colors {
	DontChange,
	Red,
	Blue,
	Grey
}

new g_bitConnectedPlayers
#define MarkUserConnected(%0)	g_bitConnectedPlayers |= 1<<(%0&31)
#define ClearUserConnected(%0)	g_bitConnectedPlayers &= ~(1<<(%0&31))
#define IsUserConnected(%0)		( g_bitConnectedPlayers & 1<<(%0&31) )

new const g_szTeamName[Colors][] = 
{
	"UNASSIGNED",
	"TERRORIST",
	"CT",
	"SPECTATOR"
}

new gmsgSayText, gmsgTeamInfo, g_iMaxPlayers
new Array:g_aStoreML

public plugin_init() 
{
	register_plugin("ColorChat", VERSION, "ConnorMcLeod")

	gmsgTeamInfo = get_user_msgid("TeamInfo")
	gmsgSayText = get_user_msgid("SayText")
	g_iMaxPlayers = get_maxplayers()
	g_aStoreML = ArrayCreate()
}

public client_putinserver(id)
{
	if( !is_user_bot(id) )
	{
		MarkUserConnected(id)
	}
}

public client_disconnect(id)
{
	ClearUserConnected(id)
}

public plugin_end()
{
	ArrayDestroy(g_aStoreML)
}

public plugin_natives()
{
	register_library("chatcolor")
	register_native("client_print_color", "client_print_color")
	register_native("register_dictionary_colored", "register_dictionary_colored")
}

// client_print_color(id, iColor=DontChange, const szMsg[], any:...)
public client_print_color(iPlugin, iParams)
{
	new id = get_param(1)

	// check if id is different from 0
	if( id )
	{
		// check player range and ingame player
		if( !IsPlayer(id) || !IsUserConnected(id) )
		{
			return 0
		}
	}

	new iColor = get_param(2)
	if( iColor > Grey )
	{
		iColor = DontChange
	}

	new szMessage[192]
	if( iColor == DontChange )
	{
		szMessage[0] = 0x04
	}
	else
	{
		szMessage[0] = 0x03
	}

	// Specific player code
	if(id)
	{
		if( iParams == 3 )
		{
			// if only 3 args are passed, no need to format the string, just retrieve it
			get_string(3, szMessage[1], charsmax(szMessage)-1)
		}
		else
		{
			// else format the string
			vdformat(szMessage[1], charsmax(szMessage)-1, 3, 4)
		}

		if( iColor )
		{
			new szTeam[11] // store current team so we can restore it
			get_user_team(id, szTeam, charsmax(szTeam))

			// set id TeamInfo in consequence
			// so SayText msg gonna show the right color
			Send_TeamInfo(id, id, g_szTeamName[iColor])

			// Send the message
			Send_SayText(id, id, szMessage)

			// restore TeamInfo
			Send_TeamInfo(id, id, szTeam)
		}
		else
		{
			Send_SayText(id, id, szMessage)
		}
		//return 1
	} 

	// Send message to all players
	else
	{
		// Figure out if at least 1 player is connected
		// so we don't send useless message if not
		// and we gonna use that player as team reference (aka SayText message sender) for color change
		new iPlayers[32], iNum
		get_players(iPlayers, iNum, "c")
		if( !iNum )
		{
			return 0
		}

		new iFool = iPlayers[0]

		new iMlNumber
		if( iParams >= 5 ) // ML can be used
		{
			new j

			// Use that array to store LANG_PLAYER args indexes, and szTemp to store ML keys
			new szTemp[64]

			for(j=4; j<iParams; j++)
			{
				// retrieve original param value and check if it's LANG_PLAYER value
				if( get_param_byref(j) == LANG_PLAYER )
				{
					// as LANG_PLAYER == -1, check if next parm string is a registered language translation
					get_string(j+1, szTemp, charsmax(szTemp))
					if( GetLangTransKey(szTemp) != TransKey_Bad )
					{
						// Store that arg as LANG_PLAYER so we can alter it later
						ArrayPushCell(g_aStoreML, j)

						// Update ML array saire so we'll know 1st if ML is used,
						// 2nd how many args we have to alterate
						iMlNumber++

						j++
					}
				}
			}
		}

		// If arraysize == 0, ML is not used
		// we can only send 1 MSG_BROADCAST message
		if( !iMlNumber )
		{
			if( iParams == 3 )
			{
				get_string(3, szMessage[1], charsmax(szMessage)-1)
			}
			else
			{
				vdformat(szMessage[1], charsmax(szMessage)-1, 3, 4)
			}

			if( iColor )
			{
				new szTeam[11]
				get_user_team(iFool, szTeam, charsmax(szTeam))
				Send_TeamInfo(0, iFool, g_szTeamName[iColor])
				Send_SayText(0, iFool, szMessage)
				Send_TeamInfo(0, iFool, szTeam)
			}
			else
			{
				Send_SayText(0, iFool, szMessage)
			}
		}

		// ML is used, we need to loop through all players,
		// format text and send a MSG_ONE_UNRELIABLE SayText message
		else
		{
			new szTeam[11], szFakeTeam[10], j
			
			if( iColor )
			{
				get_user_team(iFool, szTeam, charsmax(szTeam))
				copy(szFakeTeam, charsmax(szFakeTeam), g_szTeamName[iColor])
			}

			for( new i = 0; i < iNum; i++ )
			{
				id = iPlayers[i]

				for(j=0; j<iMlNumber; j++)
				{
					// Set all LANG_PLAYER args to player index ( = id )
					// so we can format the text for that specific player
					set_param_byref(ArrayGetCell(g_aStoreML, j), id)
				}

				// format string for specific player
				vdformat(szMessage[1], charsmax(szMessage)-1, 3, 4)

				if( iColor )
				{
					Send_TeamInfo(id, iFool, szFakeTeam)
					Send_SayText(id, iFool, szMessage)
					Send_TeamInfo(id, iFool, szTeam)
				}
				else
				{
					Send_SayText(id, iFool, szMessage)
				}
			}
			// clear the array so next ML message we don't need to figure out
			// if should use PushArray or SetArray
			ArrayClear(g_aStoreML)
		}
	}
	return 1
}

Send_TeamInfo(iReceiver, iPlayerId, szTeam[])
{
	message_begin(iReceiver ? MSG_ONE_UNRELIABLE : MSG_BROADCAST, gmsgTeamInfo, .player=iReceiver)
	write_byte(iPlayerId)
	write_string(szTeam)
	message_end()
}

Send_SayText(iReceiver, iPlayerId, szMessage[])
{
	message_begin(iReceiver ? MSG_ONE_UNRELIABLE : MSG_BROADCAST, gmsgSayText, .player=iReceiver)
	write_byte(iPlayerId)
	write_string(szMessage)
	message_end()
}

public register_dictionary_colored(iPlugin, iParams)
{
	new filename[64]
	get_string(1, filename, charsmax(filename))

	if( !register_dictionary(filename) )
	{
		return 0
	}

	new szFileName[256]
	get_localinfo("amxx_datadir", szFileName, charsmax(szFileName))
	format(szFileName, charsmax(szFileName), "%s/lang/%s", szFileName, filename)
	new fp = fopen(szFileName, "rt")
	if( !fp )
	{
		log_error(AMX_ERR_NATIVE, "Failed to open %s", szFileName)
		return 0
	}

	new szBuffer[512], szLang[3], szKey[64], szTranslation[256], TransKey:iKey

	while( !feof(fp) )
	{
		fgets(fp, szBuffer, charsmax(szBuffer))
		trim(szBuffer)

		if( szBuffer[0] == '[' )
		{
			strtok(szBuffer[1], szLang, charsmax(szLang), szBuffer, 1, ']')
		}
		else if( szBuffer[0] )
		{
			strbreak(szBuffer, szKey, charsmax(szKey), szTranslation, charsmax(szTranslation))
			iKey = GetLangTransKey(szKey)
			if( iKey != TransKey_Bad )
			{
				while( replace(szTranslation, charsmax(szTranslation), "!g", "^4") ){}
				while( replace(szTranslation, charsmax(szTranslation), "!t", "^3") ){}
				while( replace(szTranslation, charsmax(szTranslation), "!n", "^1") ){}
				AddTranslation(szLang, iKey, szTranslation[2])
			}
		}
	}
	
	fclose(fp)
	return 1
}